package com.jh.fcsm.service.area.impl;

import java.io.*;
import java.net.URLEncoder;
import java.nio.charset.StandardCharsets;
import java.text.SimpleDateFormat;
import java.util.*;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.stream.Collectors;

import com.alibaba.excel.EasyExcel;
import com.alibaba.excel.ExcelWriter;
import com.alibaba.excel.write.metadata.WriteSheet;
import com.alibaba.excel.write.metadata.fill.FillConfig;
import com.jh.fcsm.beans.area.ImportError;
import com.jh.fcsm.util.AppUserUtil;
import com.jh.fcsm.util.AreaExcelUtil;
import com.jh.fcsm.util.poi.EasyExcelUtils;
import org.apache.poi.ss.usermodel.*;
import org.apache.poi.xssf.usermodel.XSSFWorkbook;
import org.springframework.core.io.ClassPathResource;
import org.springframework.util.StringUtils;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import com.jh.fcsm.beans.area.BasicArea;
import com.jh.fcsm.beans.area.vo.BasicAreaVo;
import com.jh.fcsm.common.exception.ServiceException;
import com.jh.fcsm.common.BaseServiceImpl;
import com.jh.fcsm.util.security.UUIDUtils;
import com.jh.fcsm.dao.area.BasicAreaMapper;
import com.jh.fcsm.service.area.BasicAreaService;
import com.jh.fcsm.constant.CommonConstant;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;
import tk.mybatis.mapper.entity.Example;
import tk.mybatis.mapper.entity.Example.Criteria;

import javax.servlet.http.HttpServletResponse;

import static com.github.pagehelper.page.PageMethod.orderBy;

/**
 * 行政区划管理Service业务层处理
 *
 * @author linq
 * @date 2024-08-15
 */
@Service
@Transactional(readOnly = true)
public class BasicAreaServiceImpl extends BaseServiceImpl<BasicAreaMapper, BasicArea> implements BasicAreaService {

    private static final Logger logger = LoggerFactory.getLogger(BasicAreaServiceImpl.class);
    @Autowired
    private BasicAreaMapper basicAreaMapper;

    @Override
    @Transactional(readOnly = false)
    /**
     * 保存或更新行政区划管理
     *@param basicArea 行政区划管理对象
     *@return String 行政区划管理ID
     *@author linq
     */
    public String saveOrUpdateBasicArea(BasicArea basicArea) {
        if (basicArea == null) {
            throw new ServiceException("数据异常");
        }

        // 获取现有行政区划信息（根据 ID 查询）
        BasicArea existingAreaById = basicAreaMapper.selectByPrimaryKey(basicArea.getId());

        if (StringUtils.isEmpty(basicArea.getId())) {
            // 新增
            BasicArea existingAreaByCode = basicAreaMapper.selectByCode(basicArea.getCode());
            if (existingAreaByCode != null) {
                throw new ServiceException("行政区划代码已存在");
            }

            basicArea.setId(UUIDUtils.getUUID());
            basicArea.setYn(1);
            basicAreaMapper.insertSelective(basicArea);
        } else {
            // 更新
            if (existingAreaById != null && !existingAreaById.getCode().equals(basicArea.getCode())) {
                // 只有在代码被修改时才检查重复
                BasicArea existingAreaByCode = basicAreaMapper.selectByCode(basicArea.getCode());
                if (existingAreaByCode != null) {
                    throw new ServiceException("行政区划代码已存在");
                }
            }

            // 避免页面传入修改
            basicArea.setYn(null);
            basicAreaMapper.updateByPrimaryKeySelective(basicArea);
        }
        return basicArea.getId();
    }

    @Override
    @Transactional(readOnly = false)
    /**
     * 删除行政区划管理
     *@param ids void 行政区划管理ID
     *@author linq
     */
    public void deleteBasicArea(List<String> ids) {
        for (String id : ids) {
            BasicArea basicArea = basicAreaMapper.selectByPrimaryKey(id);
            if (basicArea == null) {
                throw new ServiceException("非法请求");
            }
            //逻辑删除
            BasicArea tembasicArea = new BasicArea();
            tembasicArea.setYn(CommonConstant.FLAG_NO);
            tembasicArea.setId(basicArea.getId());
            basicAreaMapper.updateByPrimaryKeySelective(tembasicArea);
        }
    }

    /**
     * 查询行政区划管理详情
     *
     * @param id
     * @return BasicArea
     * @author linq
     */
    @Override
    public BasicArea findById(String id) {
        return basicAreaMapper.selectByPrimaryKey(id);
    }


    /**
     * 分页查询行政区划管理
     *
     * @param basicAreaVo
     * @return PageInfo<BasicArea>
     * @author linq
     */
    @Override
    public PageInfo<BasicArea> findPageByQuery(BasicAreaVo basicAreaVo) {
        // 如果 pageNum 和 pageSize 不为 null，则进行分页
        if (basicAreaVo.getPageNum() != null && basicAreaVo.getPageSize() != null) {
            PageHelper.startPage(basicAreaVo.getPageNum(), basicAreaVo.getPageSize());
        }

        // 如果 orderColumn 和 orderValue 不为空，则进行排序
        if (!StringUtils.isEmpty(basicAreaVo.getOrderColumn()) && !StringUtils.isEmpty(basicAreaVo.getOrderValue())) {
            orderBy(basicAreaVo.getOrderColumn() + " " + basicAreaVo.getOrderValue());
        }

        Example example = new Example(BasicArea.class);
        Criteria criteria = example.createCriteria();
        criteria.andEqualTo("yn", CommonConstant.FLAG_YES);

        // 查询条件
        if (!StringUtils.isEmpty(basicAreaVo.getName())) {
            criteria.andLike("name", "%" + basicAreaVo.getName() + "%");
        }
        if (!StringUtils.isEmpty(basicAreaVo.getCode())) {
            criteria.andLike("code", basicAreaVo.getCode());
        }
        if (!StringUtils.isEmpty(basicAreaVo.getParentCode())) {
            criteria.andLike("parentCode", basicAreaVo.getParentCode());
        }

        example.setOrderByClause("code ASC");

        List<BasicArea> basicAreaList = basicAreaMapper.selectByExample(example);
        Map<String, String> temp = mapper.selectAll().stream().collect(Collectors.toMap(BasicArea::getCode, BasicArea::getName, (key1, key2) -> key2));

        return new PageInfo<>(basicAreaList);

    }


    /**
     * 导入行政区划数据
     *
     * @param file     上传的Excel文件
     * @param response
     * @throws Exception 导入失败时抛出异常
     */
    @Transactional(readOnly = false)
    public List<ImportError> importAreaData(@RequestParam("file") MultipartFile file, HttpServletResponse response) throws IOException {
        if (file == null || file.isEmpty()) {
            throw new ServiceException("上传的文件为空");
        }

        List<BasicArea> areaList;
        List<ImportError> errors = new ArrayList<>();

        // 期望的字段名称
        String[] expectedHeaders = {"序号", "行政区划代码", "行政区划名称", "父级区划代码"};

        byte[] fileBytes = file.getBytes();
        try (ByteArrayInputStream byteArrayInputStream = new ByteArrayInputStream(fileBytes)) {
            // 校验字段个数和字段名称
            boolean headersValid = validateHeaders(byteArrayInputStream, expectedHeaders);
            if (!headersValid) {
                throw new ServiceException("文件字段名称或顺序不匹配,请下载导入模板");
            }

            // 重新获取输入流
            try (ByteArrayInputStream newInputStream = new ByteArrayInputStream(fileBytes)) {
                // 导入数据
                areaList = AreaExcelUtil.importArea(newInputStream, BasicArea.class, 0, 1);
                if (areaList == null || areaList.isEmpty()) {
                    throw new ServiceException("导入的数据为空");
                }

                // 保存数据
                saveBatch(areaList, errors);
            }
        } catch (Exception e) {
            throw new ServiceException(e.getMessage());
        }


        return errors;
    }

    private boolean validateHeaders(InputStream inputStream, String[] expectedHeaders) throws IOException {
        try (Workbook workbook = new XSSFWorkbook(inputStream)) {
            Sheet sheet = workbook.getSheetAt(0);  // 获取第一个工作表
            Row headerRow = sheet.getRow(0);       // 获取标题行

            if (headerRow == null) {
                // 如果没有标题行，返回不匹配
                return false;
            }

            for (int i = 0; i < expectedHeaders.length; i++) {
                Cell cell = headerRow.getCell(i);

                // 如果单元格为空或者不是字符串类型，则返回不匹配
                if (cell == null || cell.getCellType() != CellType.STRING) {
                    return false;
                }

                String cellValue = cell.getStringCellValue().trim();
                if (!expectedHeaders[i].equals(cellValue)) {
                    // 如果标题行中的值与预期值不匹配，返回不匹配
                    return false;
                }
            }

            // 如果所有预期标题都匹配，返回匹配
            return true;
        }
    }



    private String validateArea(BasicArea area) {
        StringBuilder message = new StringBuilder();

        // 校验行政区划代码
        if (area.getCode() == null || area.getCode().isEmpty()) {
            message.append("行政区划代码不能为空;");
        } else if (area.getCode().length() != 6) {
            message.append("行政区划代码必须是6位;");
        }

        // 校验行政区划名称
        if (area.getName() == null || area.getName().isEmpty()) {
            message.append("行政区划名称不能为空;");
        } else if (area.getName().length() < 2 || area.getName().length() > 30) {
            message.append("行政区划名称长度应在2到30个字符之间;");
        }

        // 校验父级代码
        if (area.getParentCode() == null || area.getParentCode().isEmpty()) {
            message.append("父级代码不能为空;");
        } else if (!(area.getParentCode().length() == 6 || area.getParentCode().equals("0"))) {
            message.append("父级代码无效;");
        }

        return message.length() > 0 ? message.toString() : null;
    }


    @Transactional(readOnly = false)
    public void generateErrorReport(List<ImportError> errors, HttpServletResponse response) throws IOException {
        if (errors != null && !errors.isEmpty()) {
            String genFileName = "导入错误清单.xlsx";
            String templateFile = new ClassPathResource("errorTemplate.xlsx").getFile().getAbsolutePath();
            //设置响应头内容，以vnd.ms-excel方式打开数据
            response.setContentType("application/vnd.ms-excel");
            //修改响应头信息
            response.setHeader("Content-Disposition",
                    "attachment; filename=" + URLEncoder.encode(genFileName, StandardCharsets.UTF_8.name()));
            //告诉所有浏览器不要缓存
            response.setHeader("Cache-Control", "no-cache");
            response.setHeader("Pragma", "no-cache");
            //设置时间值属性的响应头信息，不要缓存
            response.setDateHeader("Expires", -1);
            //(设置输出的编码格式)指定输出到客户端的时候，这些文字使用UTF-8编码
            response.setCharacterEncoding("UTF-8");
            // 创建 ExcelWriter 实例
            ExcelWriter writer = EasyExcel
                    // 写入到
                    .write(response.getOutputStream())
                    // 指定模板
                    .withTemplate(templateFile)
                    .build();
            WriteSheet sheet = EasyExcel.writerSheet().build();
            // 获取员工工资数据
            FillConfig fillConfig = FillConfig.builder()
                    // 开启填充换行
                    .forceNewRow(true)
                    .build();
            // 执行填充操作
            writer.fill(errors, fillConfig, sheet);
            // 结束
            writer.finish();


        /*// 调用 exportExcelByModel 方法
        EasyExcelUtils.exportExcelByModel(
                errors,              // 数据列表
                "导入错误清单",         // 工作表名称
                ImportError.class,       // 数据模型类
                genFilename,              // 文件名
                response,              // HttpServletResponse
                templateFile           // 模板文件路径
        );*/
        }
        if (errors != null) {
            errors.clear();
        }
    }

    /**
     * 批量导入数据
     *
     * @param areaList
     */
    @Transactional(readOnly = false)
    public void saveBatch(List<BasicArea> areaList, List<ImportError> errors) {
        Map<String, BasicArea> codeMap = new HashMap<>();
        Map<Integer, ImportError> errorMap = new HashMap<>();
        Set<String> codes = new HashSet<>();

        for (int i = 0; i < areaList.size(); i++) {
            BasicArea area = areaList.get(i);
            String code = area.getCode();
            String name = area.getName();
            int rowIndex = i + 1;

            if (codeMap.containsKey(code)) {
                errorMap.computeIfAbsent(rowIndex, k -> new ImportError(rowIndex, code, name, area.getParentCode(), ""))
                        .appendErrorMsg("行政区划代码重复");
            } else {
                codeMap.put(code, area);
                codes.add(code);
            }
        }

        List<BasicArea> existingAreas = basicAreaMapper.selectByCodes(codes);
        Map<String, BasicArea> existingAreaMap = existingAreas.stream()
                .collect(Collectors.toMap(BasicArea::getCode, area -> area));

        List<BasicArea> toInsert = new ArrayList<>();

        for (int i = 0; i < areaList.size(); i++) {
            BasicArea area = areaList.get(i);
            String errorMessage = validateArea(area);
            int rowIndex = i + 1;

            if (errorMessage == null) {
                if (existingAreaMap.containsKey(area.getCode())) {
                    errorMap.computeIfAbsent(rowIndex, k -> new ImportError(rowIndex, area.getCode(), area.getName(), area.getParentCode(), ""))
                            .appendErrorMsg("行政区划代码已存在");
                } else if (codeMap.get(area.getCode()) != null) {
                    area.setCreateUser(AppUserUtil.getCurrentUserName());
                    area.setUpdateUser(AppUserUtil.getCurrentUserName());
                    area.setCreateUserNickname(AppUserUtil.getCurrentRealName());
                    area.setUpdateUserNickname(AppUserUtil.getCurrentRealName());
                    area.setYn(CommonConstant.FLAG_YES);
                    area.setId(UUIDUtils.getUUID());
                    toInsert.add(area);
                }
            } else {
                errorMap.computeIfAbsent(rowIndex, k -> new ImportError(rowIndex, area.getCode(), area.getName(), area.getParentCode(), ""))
                        .appendErrorMsg(errorMessage);
            }
        }

        errors.addAll(errorMap.values());

        if (!toInsert.isEmpty() && errors.isEmpty()) {
            basicAreaMapper.insertBatch(toInsert);
        }
    }





    /**
     * 导出行政区划数据
     *
     * @param areaList 要导出的行政区划数据列表
     * @param response HttpServletResponse
     * @throws IOException 如果导出过程中发生IO错误
     */
    @Transactional(readOnly = false)
    public void exportAreaData(List<BasicArea> areaList, HttpServletResponse response) throws IOException {
        if (areaList == null || areaList.isEmpty()) {
            throw new ServiceException("没有可导出的数据");
        }

        // 生成文件名中的时间戳
        String timestamp = new SimpleDateFormat("yyyyMMddHHmmss").format(new Date());
        String filename = "行政区划数据_" + timestamp + ".xlsx";

//        // 设置响应头
//        response.setContentType("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet");
//        response.addHeader("Access-Control-Allow-Origin", "*");
//        response.addHeader("Content-Disposition", "attachment; filename=\"" + URLEncoder.encode(filename, "UTF-8") + "\"");
//        response.setHeader("Access-Control-Expose-Headers", "Content-Disposition");
//        response.setCharacterEncoding("UTF-8");

        // 选择模板文件
        String templateFile = new ClassPathResource("exportTemplate.xlsx").getFile().getAbsolutePath(); // 确保这个路径正确并且文件存在

        // 调用 exportExcelByModel 方法
        EasyExcelUtils.exportExcelByModel(
                areaList,              // 数据列表
                "行政区划数据",         // 工作表名称
                BasicArea.class,       // 数据模型类
                filename,              // 文件名
                response,              // HttpServletResponse
                templateFile           // 模板文件路径
        );
    }


}
